package com.cy.pj.service.realm;

import com.cy.pj.Dao.SysMenuDao;
import com.cy.pj.Dao.SysUserDao;
import com.cy.pj.common.pojo.SysUser;
import org.apache.ibatis.javassist.expr.NewExpr;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

/**
 *定义shiro realm对象,基于此对象获取用户认证和授权信息,
 *假如将来你只做认证,不做授权,则继承AuthenticatingRealm对象即可，
 * 而AuthorizingRealm则继承了AuthenticatingRealm,所以认证和授权方法都有
 */
public class ShiroRealm extends /*AuthenticatingRealm*/ AuthorizingRealm {
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysMenuDao sysMenuDao;
    //不加@Autowired也行,就写个构造方法赋值--public ShiroRealm(SysUserDao sysUserDao) {
        //this.sysUserDao = sysUserDao;
    //}

    /**
     * 获取并封装认证信息
     * @param authenticationToken 是封装了客户端认证信息的一个令牌对象(存储了用户名和密码)
     * @return
     */
    //***规定:返回值类型AuthenticationInfo是接口，所以这是个多态,那么return的对象类型就是它的子类方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        /**authenticationToken要强转成UsernamePasswordToken类型，不然取不了用户名
         * 原因:底层定义的客户端的用户信息都封装到UsernamePasswordToken类型的token:
         * UsernamePasswordToken token = new UsernamePasswordToken( username, password );
         * token.setRememberMe(true);
         */
        //1.获取客户端提交的用户名
        //UsernamePasswordToken是AuthenticationToken的底层实现类，大转小要强转
        UsernamePasswordToken  upToken= (UsernamePasswordToken) authenticationToken;
        String username=upToken.getUsername();

        //2.基于用户名查询用户信息并校验
        SysUser user=sysUserDao.selectUserByUsername(username);
if(username==null) throw new UnknownAccountException();
if(user.getValid()==0) throw new LockedAccountException();

          //3.封装用户信息(返回值是接口，不能new,用ctrl看它底层有哪些子类)
        //credentialsSalt的类型是ByteSource,再用ctrl看ByteSource的底层有哪些方法，
        //它的底层有静态类Util,可以直接调用，选其中的方法
        ByteSource credentialsSalt=ByteSource.Util.bytes(user.getSalt());
        SimpleAuthenticationInfo info=
        new SimpleAuthenticationInfo( user,//principal用户身份(基于业务设置)
                user.getPassword(),//hashedCredentials 已加密的凭证(密码)
                credentialsSalt,//credentialsSalt 做了编码处理的加密盐值对象
                this.getName());//realmName 当前类的名称

        //基础:当类的返回值类型是接口时，那就是个多态，返回值对象只能是它的实现类和子类
        return info;
    }

    /**重写此方法的目的是--底层对用户输入的登陆密码进行加密，需要算法*/
    @Override
    public CredentialsMatcher getCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher=
                new HashedCredentialsMatcher("MD5");
        //设置加密一次
        credentialsMatcher.setHashIterations(1);
        return credentialsMatcher ;
    }
    //也可以在构造方法中通过调用set方法设置加密策略
//    public ShiroRealm(){
//        HashedCredentialsMatcher credentialsMatcher=
//                new HashedCredentialsMatcher("MD5");
//        credentialsMatcher.setHashIterations(1);
//        setCredentialsMatcher(credentialsMatcher);
//
//}

    /**获取并封装授权信息 */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principalCollection) {
        System.out.println("==doGetAuthorizationInfo==");
       //1.获取登录用户
        //底层已经把认证方法的principal:user用户身份传给了授权方法,所以要从里面获取
SysUser user= (SysUser) principalCollection.getPrimaryPrincipal();
       //2.基于登录用户id,查询用户权限
        Set<String> permissionSet= sysMenuDao.selectUserPermissions(user.getId());
        //3.封装用户权限信息并返回
        //SimpleAuthorizationInfo是AuthorizationInfo的底层实现类
        SimpleAuthorizationInfo Info= new SimpleAuthorizationInfo();
         Info.setStringPermissions(permissionSet);

        return Info;
    }
}

